package aufgabe_2_test.try02;

import static akka.actor.Actors.remote;
import static aufgabe_2_test.try02.RhoMessage.MsgType.*;
import java.math.BigInteger;
import java.nio.channels.ClosedChannelException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;

import scala.actors.threadpool.TimeUnit;
import akka.actor.ActorRef;
import akka.actor.UntypedActor;
import akka.remoteinterface.RemoteServerModule;

public class Master extends  UntypedActor{
	private static int port = 1999;
	private static RemoteServerModule remoteSupport;
	private ArrayList<ActorRef> workers;
	private ArrayList<BigInteger> primes;
	private BigInteger N;
	private BigInteger div;
	private static String loc = "141.22.27.102";
	private static Gui_Master gui;
	private boolean finished;
	private long sum_CpuTime;
	private long requestStartTime;
	private BigInteger rhoSteps;
	public Master() {
		N = BigInteger.ONE;
		div = BigInteger.ONE;
		workers = new ArrayList<ActorRef>();
		primes = new ArrayList<BigInteger>();
		finished = false;
		rhoSteps = BigInteger.ZERO;
	}

	@Override
	public void onReceive(Object o) throws Exception {
		if(o instanceof RhoRequestMaster){ // new Start Message
			//reset all variables
			int k = 0;
			finished = false;
			primes.clear();
			sum_CpuTime = 0;
			rhoSteps = BigInteger.ZERO;
			RhoRequestMaster req = (RhoRequestMaster) o;			
			N = req.getN();
			div = new BigInteger(N.toByteArray());
			System.out.println("Master: Auftrag erhalten");
			ActorRef me = this.getContext();
			//create worker
			for (WorkerObject w : req.workerList) {
				for (int i = 0; i < w.getAmountWorkers(); i++, k++) {
					workers.add(remote().actorFor(Worker.class.getName(),w.getIdentifier(), w.getPort()));
					workers.get(k).tell(new RhoMessage(req.getN(), RhoRequestWorker,false), me);
				}
			}
			System.out.println("Master: Auftrag verteilt!");
			requestStartTime = new Date().getTime();
		}else if(o instanceof RhoMessage){// new Message from a Worker
			RhoMessage rm = (RhoMessage) o;
			if(rm.mt == RhoGetStatus){ // StatusMessage -> apply statistics
				sum_CpuTime += rm.cpuTime;
				rhoSteps = rhoSteps.add(rm.getN());
				workers.remove(getContext().getSender().get());
				gui.updateTable(sum_CpuTime+" ms. RS: "+rhoSteps);
			}else if(rm.mt != RhoTest && !finished){ // add Prime to list.
				addPrime(rm.getN(), rm.getCpuTime());
			}
//			TimeUnit.SECONDS.sleep(1);
		}else{
			System.out.println("Nachrichtentyp unbekannt");
		}
	}
	/**
	 * Add a Prime Number to the List of found Primenumbers.
	 * @param p the prime number
	 * @param cpuTimeOf_p time spend searching for this prime number
	 */
	public synchronized void addPrime(BigInteger p, long cpuTimeOf_p){
		ActorRef me = this.getContext();
		if(p.compareTo(BigInteger.ONE) > 0 && div.mod(p).equals(BigInteger.ZERO)){ // check if still possible
			div = div.divide(p);
			primes.add(p);
			System.out.println("Master: got Prime = "+p+" div: "+div + " cpu time: " +cpuTimeOf_p);
			if(div.equals(BigInteger.ONE)){ // finished ... sort primes and inform all Worker
				Collections.sort(primes);
				finished = true;
				BigInteger j = BigInteger.ONE;
				String factors = "";
				for(int i = 0; i < primes.size(); i++){
					j = j.multiply(primes.get(i));
					if(i != 0){
						factors += "*";
						System.out.print("*");
					}
					factors += primes.get(i);
					System.out.print(primes.get(i));
				}
				System.out.println(" = "+j);
				gui.addAResult(N, factors, "-", ((new Date().getTime())-requestStartTime)+"ms");
				// alle Workers anhalten!
				RhoMessage r = new RhoMessage(null,RhoGoSleep,true); // let them send all messages and sleep
				for(ActorRef ar : workers){
					ar.tell(r,me);
				}
			}else{
				RhoMessage r = new RhoMessage(div,RhoFoundPrime,true); //inform Worker about a found Prime, so they will have to start with the next number
				for(ActorRef ar : workers){
					ar.tell(r,me);
					System.out.println("worker: " + ar);
				}
			}
		}
	}

	public static void main(String[] args) {
		remoteSupport  = remote().start(loc, port);
		System.out.println("Master gestartet");
		gui = new Gui_Master();
		gui.setVisible(true);
		
//		ActorRef master = remote().actorFor(Master.class.getName(),  "localhost", 1999);
//		ArrayList<WorkerObject> tmpl = new ArrayList<WorkerObject>();
//		tmpl.add(new WorkerObject("localhost", 2000, 1));
//		RhoRequestMaster req = new RhoRequestMaster(new BigInteger("8806715679"), tmpl);
//		master.tell(req);
	}
}
